// (c) Copyright 2013-2014 Stephen Gold <sgold@sonic.net>
// Distributed under the terms of the GNU General Public License

/*
 This file is part of the Bats Game.

 The Bats Game is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by the
 Free Software Foundation, either version 3 of the License, or (at your
 option) any later version.

 The Bats Game is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.

 You should have received a copy of the GNU General Public License
 along with the Bats Game.  If not, see <http://www.gnu.org/licenses/>.
 */
package bats.unit;

import com.jme3.bullet.control.KinematicRagdollControl;
import com.jme3.math.Quaternion;
import com.jme3.scene.Spatial;
import java.util.logging.Logger;

/**
 * A kinematic ragdoll control for a "sinbad" unit.
 * <p>
 * Each instance is enabled at creation.
 *
 * @author Stephen Gold <sgold@sonic.net>
 */
public class SinbadControl
        extends KinematicRagdollControl {
    // *************************************************************************
    // constants

    /**
     * weight threshold
     */
    final private static float wThreshold = 0.5f;
    /**
     * message logger for this class
     */
    final private static Logger logger2 =
            Logger.getLogger(SinbadControl.class.getName());
    // *************************************************************************
    // constructors

    /**
     * Instantiate an enabled control for an unimpaired sinbad.
     */
    public SinbadControl() {
        super(wThreshold);

        addBoneName("Root");

        addBoneName("Waist");
        addBoneName("Stomach");
        addBoneName("Chest");
        addBoneName("Neck");

        addBonePair("Clavicle");
        addBonePair("Humerus");
        addBonePair("Ulna");
        addBonePair("Hand");

        addBonePair("Thigh");
        addBonePair("Calf");
        addBonePair("Foot");
    }
    // *************************************************************************
    // KinematicRagdollControl methods

    /**
     * Alter the controlled spatial. Do not call from user code!
     *
     * @param newSpatial
     */
    @Override
    public void setSpatial(Spatial newSpatial) {
        if (newSpatial != null) {
            /*
             * Work around a bug which throws an assertion error in
             * JacobianEntry.init() for certain model orientations.
             */
            Quaternion orientation = newSpatial.getLocalRotation().clone();
            if (orientation.getX() == 0f && orientation.getZ() == 0f) {
                float y = orientation.getY();
                float w = orientation.getW();
                if (y == w) {
                    orientation.set(0f, 1f, 0f, 1.0001f);
                    orientation.normalizeLocal();
                } else if (y == -w) {
                    orientation.set(0f, 1f, 0f, -1.0001f);
                    orientation.normalizeLocal();
                }
                newSpatial.setLocalRotation(orientation);
            }
        }

        super.setSpatial(newSpatial);

        if (newSpatial != null) {
            setEventDispatchImpulseThreshold(1f);
            /*
             * X = bend, Y = twist, Z = side-to-side
             */
            setJointLimit("Waist", 0.3f, -0.3f, 0.2f, -0.2f, 0.5f, -0.5f);
            setJointLimit("Stomach", 1f, -1f, 0.1f, -0.1f, 0.5f, -0.5f);
            setJointLimit("Chest", 0.2f, -0.2f, 0.5f, -0.5f, 0.4f, -0.4f);
            setJointLimit("Neck", 1f, -1f, 0.1f, -0.1f, 0.5f, -0.5f);

            setJointPairLimit("Clavicle", 0.5f, -0.5f, 0.1f, -0.1f, 0.5f, -0.5f);
            setJointPairLimit("Humerus", 0.6f, -0.6f, 0.5f, -0.5f, 1f, -1f);
            setJointPairLimit("Ulna", 0.1f, -0.1f, 0.4f, -0.4f, 1f, -1f);
            setJointPairLimit("Hand", 1f, -1f, 0.8f, -0.8f, 0.2f, -0.2f);

            setJointPairLimit("Thigh", 0.5f, -0.5f, 0.1f, -0.1f, 0.3f, -0.3f);
            setJointPairLimit("Calf", 1f, -1f, 0.3f, -0.3f, 0.1f, -0.1f);
            setJointPairLimit("Foot", 0.8f, -0.8f, 0.3f, -0.3f, 1f, -1f);
        }
    }
    // *************************************************************************
    // private methods

    /**
     * Add a pair of bilaterally symmetric bones to this control.
     *
     * @param namePrefix (not null)
     */
    private void addBonePair(String namePrefix) {
        assert namePrefix != null;

        addBoneName(namePrefix + ".L");
        addBoneName(namePrefix + ".R");
    }

    /**
     * Set limits for a pair of bilaterally symmetric joints.
     *
     * @param namePrefix (not null)
     * @param maxX maximum rotation on the X axis (in radians)
     * @param minX minimum rotation on the X axis (in radians)
     * @param maxY maximum rotation on the Y axis (in radians)
     * @param minY minimum rotation on the Y axis (in radians)
     * @param maxZ maximum rotation on the Z axis (in radians)
     * @param minZ minimum rotation on the Z axis (in radians)
     */
    private void setJointPairLimit(String namePrefix, float maxX, float minX,
            float maxY, float minY, float maxZ, float minZ) {
        assert namePrefix != null;
        assert maxX >= minX;
        assert maxY >= minY;
        assert maxZ >= minZ;

        setJointLimit(namePrefix + ".L",
                maxX, minX, maxY, minY, maxZ, minZ);
        setJointLimit(namePrefix + ".R",
                maxX, minX, -minY, -maxY, -minZ, -maxZ);
    }
}